package com.caishi.lkx.user.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.caishi.lkx.common.config.DiamondConfig;
import com.caishi.lkx.common.context.UserContext;
import com.caishi.lkx.common.ienum.PlatformType;
import com.caishi.lkx.common.mapper.RedisCopyDataMapper;
import com.caishi.lkx.exam.mapper.NoteMapper;
import com.caishi.lkx.exam.mapper.QuestionMapper;
import com.caishi.lkx.exam.mapper.WeekRankMapper;
import com.caishi.lkx.exam.model.CategoryModel;
import com.caishi.lkx.exam.model.NoteModel;
import com.caishi.lkx.exam.service.ICategoryService;
import com.caishi.lkx.exam.service.IUserCountRecordsService;
import com.caishi.lkx.user.dto.*;
import com.caishi.lkx.user.ienum.status.UserStatus;
import com.caishi.lkx.user.ienum.type.*;
import com.caishi.lkx.user.mapper.LoginInfoMapper;
import com.caishi.lkx.user.mapper.MemberMapper;
import com.caishi.lkx.user.model.*;
import com.caishi.lkx.user.service.*;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBaseMapper;
import com.zzw.common.entity.impl.BSDUAbsEntity;
import com.zzw.common.exception.BizException;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.file.OssPlugins;
import com.zzw.common.threadpool.SysThreadPool;
import com.zzw.common.utils.MD5Util;

import com.caishi.lkx.exam.records.RecordsGetDataService;
import com.caishi.lkx.exam.records.RecordsSetDataService;
import com.caishi.lkx.exam.records.dto.RecordsCountRecordDto;

import com.caishi.lkx.user.UserResultCode;

import com.caishi.lkx.user.login.LoginService;
import com.caishi.lkx.user.login.LoginServiceComposite;

import com.caishi.lkx.user.userlog.NormalLogRecordsService;
import com.caishi.lkx.user.userlog.model.UserLogType;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.caishi.lkx.common.fliter.TokenFilter.OSS_DOMAIN;
import static com.caishi.lkx.common.fliter.TokenFilter.OSS_USER_INFO;


/**
 * @author by keray
 * date:2021/4/9 2:59 下午
 */
@Service("memberService")
@Slf4j
//@DubboService(interfaceClass = IMemberBaseService.class)
public class MemberServiceImpl implements IMemberService {

    private static final String GENERATE_STU_NO_KEY = "generate-stu-no";
    private static final String GENERATE_NAME_NO_KEY = "generate-name-no";
    private static final String VERS_USERINFO_URL = "https://langrunkaoyan.cqvip.com/Api/User/GetSynchroUserInfo?username=";
    @Resource
    private DiamondConfig diamondConfig;
    @Resource
    private MemberMapper memberMapper;

    @Resource
    private UserServiceImpl userService;

    @Resource(name = "persistenceRedisTemplate")
    private RedisTemplate<String, Integer> generateStuNo;
    @Resource
    private IUserCountRecordsService userCountRecordsService;
    @Resource
    private RedisCopyDataMapper redisCopyDataMapper;

    @Resource
    private LoginServiceComposite loginServiceComposite;
    @Resource
    private IGroupService groupService;
    @Resource
    private IUserGroupReService userGroupReService;
    @Resource
    private ICategoryService categoryService;
    @Resource
    private RecordsGetDataService recordsGetDataService;
    @Resource
    private NoteMapper noteMapper;
    @Resource
    private IUserQuestionRecordsService userQuestionRecordsService;
    @Resource
    private QuestionMapper questionMapper;
    @Resource
    private OssPlugins cdnOssPlugins;
    @Resource
    private WeekRankMapper weekRankMapper;
    @Resource
    private LoginInfoMapper loginInfoMapper;
    @Resource
    private IDepartmentService departmentService;

    @Resource
    private ILoginInfoService loginInfoService;

    @Resource
    private NormalLogRecordsService normalLogRecordsService;

    @Resource
    private RecordsSetDataService recordsSetDataService;
    @Resource
    protected UserContext userContext;

    @Override
    public IBaseMapper<MemberModel, String> getMapper() {
        return memberMapper;
    }

    @Override
    public IUserService<UserModel> baseService() {
        return userService;
    }

    @PostConstruct
    public void initRedisStudentNo() {
        if (!Boolean.TRUE.equals(generateStuNo.hasKey(GENERATE_STU_NO_KEY))) {
            String val = redisCopyDataMapper.data(GENERATE_STU_NO_KEY);
            if (val != null) {
                log.info("系统启动，设置最新学号记录到Redis:{}", val);
                generateStuNo.opsForValue().set(GENERATE_STU_NO_KEY, Integer.parseInt(val));
            }
        }
        if (!Boolean.TRUE.equals(generateStuNo.hasKey(GENERATE_NAME_NO_KEY))) {
            String val = redisCopyDataMapper.data(GENERATE_NAME_NO_KEY);
            if (val != null) {
                log.info("系统启动，设置最新用户记录到Redis:{}", val);
                generateStuNo.opsForValue().set(GENERATE_NAME_NO_KEY, Integer.parseInt(val));
            }
        }
    }

    @PreDestroy
    public void saveRedisStudentNo() {
        try {
            log.info("系统关闭，持久化用户学号记录到数据库");
            Integer oldData = generateStuNo.opsForValue().get(GENERATE_STU_NO_KEY);
            if (oldData != null) {
                redisCopyDataMapper.addData(GENERATE_STU_NO_KEY, oldData.toString());
            }
            oldData = generateStuNo.opsForValue().get(GENERATE_NAME_NO_KEY);
            if (oldData != null) {
                redisCopyDataMapper.addData(GENERATE_NAME_NO_KEY, oldData.toString());
            }
        } catch (Exception e) {
            log.error("系统关闭，持久化用户学号记录到数据库 异常", e);
        }
    }

    @Override
    public MemberModel register(LoginDto loginDto, HttpServletRequest request, HttpServletResponse response) throws BizException {
        MemberModel member = new MemberModel();
        member.addUserType(UserType.member);
        member.setMobile(loginDto.getUserAccount());
        member.setGroupId(loginDto.getGroupId());
        // 设置渠道
        String channelCode = request.getHeader("channelCode");
        if (StrUtil.isNotBlank(channelCode)) {
            try {
                UserChannelType type = UserChannelType.valueOf(channelCode);
                member.setChannelType(type);
                member.setChannelPointer(loginDto.getDeviceType().name());
            } catch (Exception e) {
                member.setChannelType(UserChannelType.pcAdmin7);
                member.setChannelPointer(channelCode);
            }
        } else {
            member.setChannelType(UserChannelType.pcAdmin7);
            member.setChannelPointer(loginDto.getDeviceType().name());
        }
        return (MemberModel) loginServiceComposite.register(LoginInfoModel.builder().account(loginDto.getUserAccount()).certificate(loginDto.getCredential()).loginType(loginDto.getType()).build(), member);
    }

    @Override
    public MemberModel register(MemberModel userModel) {
        if (userModel.getUserType() == 0) {
            userModel.addUserType(UserType.member);
        }
        registerProcess(userModel);
        UserModel baseModel = userService.register(userModel.toUserModel());
        userModel.userInfoCopy(baseModel);
        var sourceType = UserLogSourceType.PcZhiYe;
        normalLogRecordsService.addTodayData(UserLogType.registerLog, 0L, sourceType, null, 1, 1);
        return registerCheck(baseModel, userModel);
    }

    private MemberModel registerCheck(UserModel baseUser, MemberModel model) {
        if (memberMapper.contains(baseUser.getId())) {
            this.simpleUpdate(model.clearUserModelMessage());
            return model.userInfoCopy(baseUser);
        }
        model = model.clearUserModelMessage();
        MemberModel memberModel = this.insert(model).userInfoCopy(baseUser);

        return memberModel;
    }

    private void registerProcess(MemberModel model) {
        Long studentNo;
        if (StrUtil.isBlank(model.getStuNo()) && (model.getUserType().equals(UserType.member.getValue()) )) {
            // 添加自增学号
            studentNo = generateStuNo.opsForValue().increment(GENERATE_STU_NO_KEY);
            if (studentNo == null) {
                studentNo = 1L;
            }
            studentNo += 10_0000;
            String no = "STU" + studentNo;
            if (studentNo < 1000000) {
                no = "STU" + NumberUtil.decimalFormat("0000000", studentNo);
            }
            model.setStuNo(no);
        }
        if (StrUtil.isBlank(model.getName())) {
            // 添加自增学号
            studentNo = generateStuNo.opsForValue().increment(GENERATE_NAME_NO_KEY);
            if (studentNo == null) {
                studentNo = 1L;
            }
            var a = studentNo.toString();
            if (studentNo < 10000) {
                a = NumberUtil.decimalFormat("0000", studentNo);
            }
            model.setName("学员" + a);
        }
    }

    @Override
    public String no(String memberId) {
        MemberModel memberModel = this.selectOne(Wrappers.<MemberModel>lambdaQuery().select(MemberModel::getStuNo).eq(MemberModel::getId, memberId));
        return memberModel == null ? null : memberModel.getStuNo();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchImportGroupMembers(List<MemberModel> memberModels, String defaultPass) throws BizException {
        for (MemberModel memberModel : memberModels) {
            this.addGroupMember(memberModel, defaultPass);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addGroupMember(MemberModel memberModel, String defaultPass) throws BizException {
        Assert.notEmpty(memberModel.getStuNo());
        Assert.notEmpty(memberModel.getName());
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCompletion(int status) {
                if (!Thread.currentThread().isInterrupted()) {
                    userService.updateUserLoginVersion(memberModel.getId());
                    return;
                }
                throw new BizRuntimeException();
            }
        });
        return loginServiceComposite.register(LoginInfoModel.builder().account(memberModel.getStuNo()).loginType(UserAccountType.group).certificate(LoginService.passwordEncryption(StrUtil.isBlank(defaultPass) ? memberModel.getStuNo() : defaultPass)).build(), memberModel).getId();
    }

    @Override
    public IPage<MemberModel> search(Map<String, Object> param, Page<MemberModel> page, boolean total) {
        Future<Long> future = CompletableFuture.supplyAsync(() -> null);
        if (total) {
            future = SysThreadPool.submit(() -> memberMapper.searchUserCount(param));
        }
        List<MemberModel> adminUserModels = memberMapper.searchUser(page.offset(), page.getSize(), param, page.getOrders().stream().filter(OrderItem::isAsc).map(OrderItem::getColumn).collect(Collectors.toList()), page.getOrders().stream().filter(o -> !o.isAsc()).map(OrderItem::getColumn).collect(Collectors.toList()));
        try {
            Long count = future.get();
            return page.setRecords(adminUserModels).setTotal(count == null ? page.getRecords().size() : count);
        } catch (InterruptedException | ExecutionException e) {
            throw new BizRuntimeException(e);
        }
    }

    @Override
    public Boolean bindGroup(String currentUserId, Long groupId) {

        return false;
    }


    @Override
    public Boolean bindBack(String userId, Long groupId, LocalDateTime expireTime) {
        return bindUserGroup(userId, groupId, expireTime);
    }

    @Override
    public Boolean bindGroupCode(MemberModel memberModel, String groupCode) {

        return false;
    }

    public Boolean bindVers7AndKaoyan(String currentUserId, Long groupId, LocalDateTime expireTime) {
        MemberModel memberModel = new MemberModel();
        memberModel.setId(currentUserId);
        memberModel.setGroupId(groupId);
        memberModel.setExpireTime(expireTime);
        memberModel.setKaoyanExpireTime(expireTime);
        UserModel userModel = userService.getById(currentUserId);
        UserType[] userTypes = userModel.getUserTypes();
        if (Arrays.asList(userTypes).contains(UserType.member)) {
            userModel.delUserType(UserType.member);
        }

        userService.update(userModel);
        return memberMapper.updateById(memberModel) > 0 ? this.sendVers6(currentUserId, null, null) : false;
    }

    @Transactional
    public Boolean bindUserGroup(String currentUserId, Long groupId, LocalDateTime expireTime) {
//        MemberModel memberModel = new MemberModel();
//        memberModel.setId(currentUserId);
//        memberModel.setGroupId(groupId);
//        GroupModel groupModel = groupService.getById(groupId);
//        if (userContext.currentPlatform().equals(PlatformType.KAOYAN)) {
//            if (null == groupModel.getKaoyanExpireTime() || groupModel.getKaoyanExpireTime().isBefore(LocalDate.now())) {
//                throw new BizRuntimeException(UserResultCode.groupExpired);
//            }
//        } else {
//            if (null == groupModel.getExpireTime() || groupModel.getExpireTime().isBefore(LocalDate.now())) {
//                throw new BizRuntimeException(UserResultCode.groupExpired);
//            }
//        }
//
//        if (expireTime == null) {
//            LocalDate userVers7ExpireTime = groupService.getUserExpireTime(groupModel, PlatformType.VERS7);
//            LocalDate userKaoyanExpireTime = groupService.getUserExpireTime(groupModel, PlatformType.KAOYAN);
//            memberModel.setKaoyanExpireTime(null);
//            memberModel.setExpireTime(null);
//            if (null != userVers7ExpireTime) {
//                memberModel.setExpireTime(userVers7ExpireTime.atTime(23, 59, 59));
//            }
//            if (null != userKaoyanExpireTime) {
//                memberModel.setKaoyanExpireTime(userKaoyanExpireTime.atTime(23, 59, 59));
//            }
//        } else {
//            if (userContext.currentPlatform().equals(PlatformType.VERS7)) {
//                memberModel.setExpireTime(expireTime);
//            } else {
//                memberModel.setKaoyanExpireTime(expireTime);
//            }
//
//        }
//        UserModel userModel = userService.getById(currentUserId);
//        UserType[] userTypes = userModel.getUserTypes();
//        if (Arrays.asList(userTypes).contains(UserType.member)) {
//            userModel.delUserType(UserType.member);
//        }
//        userModel.addUserType(UserType.groupMember);
//        userService.update(userModel);
//        return memberMapper.updateById(memberModel) > 0 ? this.sendVers6(currentUserId, null, null) : false;
        return null;
    }

    @Override
    public Boolean unBindGroup(String currentUserId) {
        UserModel userModel = userService.getById(currentUserId);
        userModel.setId(currentUserId);
        Optional<UserType> first = Arrays.stream(userModel.getUserTypes()).filter(f -> f.equals(UserType.member)).findFirst();
        if (first.isEmpty()) {
            userModel.addUserType(UserType.member);
        }
        userService.update(userModel);
        return memberMapper.update(null, Wrappers.<MemberModel>lambdaUpdate().eq(MemberModel::getId, currentUserId).setSql(" group_id = null , expire_time = null ,kaoyan_expire_time = null")) > 0 ? this.sendVers6(currentUserId, null, null) : false;
    }

    @Override
    public Boolean updateExpireDate(String currentUserId, Long localGroupId) {
        MemberModel memberModel = memberMapper.selectById(currentUserId);
        if (!memberModel.getGroupId().equals(localGroupId)) {
            throw new BizRuntimeException(UserResultCode.groupNotMatch);
        }
        return updateMemberGroupExpire(currentUserId, memberModel);
    }

    @Override
    public Boolean updateExpireDate(String userId) {
        MemberModel memberModel = memberMapper.selectById(userId);
        return updateMemberGroupExpire(userId, memberModel);
    }

    private Boolean updateMemberGroupExpire(String userId, MemberModel memberModel) {
        if (null == memberModel.getGroupId()) {
            throw new BizRuntimeException(UserResultCode.groupExpired);
        }
        GroupModel group = groupService.getById(memberModel.getGroupId());

        LocalDate userExpireTime = group.getExpireTime();
        LocalTime time = LocalTime.of(23, 59, 59);
        LocalDateTime dateTime = LocalDateTime.of(userExpireTime, time);
        return this.update(null, Wrappers.<MemberModel>lambdaUpdate().set(MemberModel::getExpireTime, dateTime).eq(MemberModel::getId, userId));
    }

    @Override
    public Boolean importMember(MultipartFile file, Long groupId, UserChannelType channelType, String createdBy) throws IOException {
//        GroupModel group = groupService.getById(groupId);
//        PlatformType platformType = userContext.currentPlatform();
//        if (platformType.equals(PlatformType.VERS7) && group.getExpireTime().atTime(23, 59, 59).isBefore(LocalDateTime.now())) {
//            //机构过期
//            throw new BizRuntimeException(UserResultCode.groupExpired);
//        }
//        if (platformType.equals(PlatformType.KAOYAN) && group.getKaoyanExpireTime().atTime(23, 59, 59).isBefore(LocalDateTime.now())) {
//            //机构过期
//            throw new BizRuntimeException(UserResultCode.groupExpired);
//        }
//        EasyExcel.read(file.getInputStream(), MemberImportVo.class, new ImportMemberListener(loginServiceComposite, groupService, UserType.groupMember, group, channelType, platformType, createdBy, UserAccountType.username, departmentService, loginInfoService)).sheet().doRead();
        return true;
    }

    @Override
    public Long currentGroupId(String currentUserId) {
        MemberModel member = this.getById(currentUserId);
        if (null == member || null == member.getGroupId()) {
            return null;
        }
        return member.getGroupId();
    }

    @Override
    public UserRecordsVo getUserRecordsByCategoryId(Long categoryId, String userId) {
        UserRecordsVo userRecordsVo = new UserRecordsVo();
        //获取科目详情
        CategoryModel categoryModel = categoryService.getById(categoryId);
        if (null == categoryModel) {
            throw new BizRuntimeException("未存在该科目");
        }
        //获取科目下的总题数
        Long questionCount = (long) categoryService.fetchCategoryQuestionCount(categoryId);
        userRecordsVo.setQCount(questionCount);
        //获取用户科目下所做题数
        RecordsCountRecordDto categoryCountRecordsResult = recordsGetDataService.getCategoryCountRecordsResult(userId, categoryModel.getIndustryId(), categoryModel.getExamId(), categoryId);
        int dc = categoryCountRecordsResult.getDc();
        int ec = categoryCountRecordsResult.getEc();
        userRecordsVo.setDoCount(dc);
        //计算成功率
        int sucessCount = Math.max(0, (dc - ec));
        if ((dc - ec) < 0) {
            categoryCountRecordsResult.setDc(ec);
            recordsSetDataService.setCategoryCountRecordsData(userId, categoryCountRecordsResult, categoryModel.getIndustryId(), categoryModel.getExamId(), categoryId);
        }
        int success = 0;
        if (dc != 0) {
            success = BigDecimal.valueOf(sucessCount).divide(BigDecimal.valueOf(dc), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).intValue();
        }

        userRecordsVo.setSuccess(success);
        //获取排名
        Long rank = weekRankMapper.getUserWeekRank(userId, categoryId);
        userRecordsVo.setRank(rank);
        //获取收藏 错题 笔记
        userRecordsVo.setCc(categoryCountRecordsResult.getCc());
        userRecordsVo.setEc(ec);
        Long noteCount = noteMapper.selectCount(Wrappers.<NoteModel>lambdaQuery().eq(BSDUAbsEntity::getCreatedBy, userId).eq(NoteModel::getCategoryId, categoryId));
        userRecordsVo.setNoteCount(noteCount);
        //获取各个题型得分
        List<UserQuestionRecordsVo> userQuestionRecordsVo = this.getUserQuestionRecordsVo(categoryId, userId);
        userRecordsVo.setQuestionRecords(userQuestionRecordsVo);
        //设置生成学情时间
        userRecordsVo.setCreated(LocalDateTime.now());
        return userRecordsVo;
    }

    @Override
    @Transactional
    public Boolean addMember(Long groupId, String userId, RegisterMemberDto memberDto) throws BizException {
        MemberModel memberModel = new MemberModel();
        return saveMember(memberModel, new LoginInfoModel(), memberDto, groupId, userId);
    }

    @Override
    public Boolean addMemberForPc6(Long groupId, String userId, RegisterMemberDto memberDto) throws BizException {
        MemberModel memberModel = new MemberModel();
        memberModel.setCreatedTime(memberDto.getCreateTime());

        memberModel.addUserType(UserType.member);

        return owner(MemberServiceImpl.class).saveMember(memberModel, new LoginInfoModel(), memberDto, groupId, userId);
    }

    @Override
    public Boolean addMemberForYkt(String id, String userId, RegisterMemberDto memberDto) throws BizException {
        MemberModel memberModel = new MemberModel();
        memberModel.setCreatedTime(memberDto.getCreateTime());
        memberModel.addUserType(UserType.member);
        memberModel.setId(id);
        return owner(MemberServiceImpl.class).saveMember(memberModel, new LoginInfoModel(), memberDto, null, userId);
    }

    @Override
    public IPage<IndexMemberSearchVo> searchIndex(Page<IndexMemberSearchVo> page, Map<String, Object> searchVo) {
        return memberMapper.searchIndex(page, searchVo);
    }

    @Override
    @Transactional
    public void deleteUser(List<String> ids, Long groupId) {
        memberMapper.update(null, Wrappers.<MemberModel>lambdaUpdate().in(MemberModel::getId, ids).eq(MemberModel::getGroupId, groupId).setSql(" group_id = null , expire_time = null,department_id=null"));
//        userService.update(null,Wrappers.<UserModel>lambdaUpdate().in(UserModel::getId,ids).eq())
        List<UserModel> userModels = userService.selectList(Wrappers.<UserModel>lambdaQuery().in(UserModel::getId, ids).select(UserModel::getId, UserModel::getUserType));
        userModels.forEach(e -> {
            UserType[] userTypes = e.getUserTypes();
            if (Arrays.stream(userTypes).noneMatch(a -> a.equals(UserType.member))) {
                e.addUserType(UserType.member);
            }
            userService.update(e);
        });
        ids.forEach(e -> {
            try {
                userGroupReService.unBind(e);
                this.sendVers6(e, null, null);
            } catch (Exception ex) {
                log.error("{}用户解绑问题出现问题", e, ex);
                throw new BizRuntimeException("删除失败");
            }
        });

    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean saveMember(MemberModel memberModel, LoginInfoModel loginInfoModel, RegisterMemberDto memberDto, Long groupId, String userId) throws BizException {
        memberModel.setDepartmentId(memberDto.getDepartmentId());
        memberModel.setSex(memberDto.getSex());
        memberModel.setEmail(memberDto.getEmail());
        memberModel.setStuNo(memberDto.getStuNo());
        if (userContext.currentPlatform().equals(PlatformType.VERS7)) {
            memberModel.setExpireTime(memberDto.getExpireTime().plusHours(23).plusSeconds(59).plusMinutes(59));
        } else {
            memberModel.setKaoyanExpireTime(memberDto.getExpireTime().plusHours(23).plusSeconds(59).plusMinutes(59));
        }
        memberModel.setMobile(memberDto.getMobile());
        memberModel.setGroupId(groupId);
        memberModel.setCreatedBy(userId);
        memberModel.setChannelType(memberDto.getType());
        memberModel.setName(memberDto.getName());
        memberModel.setChannelPointer(userId);
        memberModel.setCard(memberDto.getCard());
        memberModel.setDescription(memberDto.getDescription());
        loginInfoModel.setCertificate(memberDto.getPassword());
        loginInfoModel.setLoginType(UserAccountType.username);
        loginInfoModel.setAccount(memberDto.getUserName());
        IUserModel<?> model = loginServiceComposite.register(loginInfoModel, memberModel);
//        if (UserType.intHaveType(memberModel.getUserType(), UserType.groupMember)) {
//            loginInfoService.userAddNewLogin(loginInfoModel.getAccount(), loginInfoModel.getCertificate(), UserAccountType.group, model.getId());
//        }
        return null != model;
    }

    private List<UserQuestionRecordsVo> getUserQuestionRecordsVo(Long categoryId, String userId) {
        UserQuestionRecordsModel recordsModel = userQuestionRecordsService.selectOne(Wrappers.<UserQuestionRecordsModel>lambdaQuery().eq(UserQuestionRecordsModel::getCategoryId, categoryId).eq(UserQuestionRecordsModel::getUserId, userId));
        if (null == recordsModel) {
            return null;
        }
        List<UserQuestionRecordsVo> vos = new ArrayList<>();
        Map<String, JSONObject> questionCount = recordsModel.getQuestionCount();
        Set<Map.Entry<String, JSONObject>> entries = questionCount.entrySet();
        entries.parallelStream().forEach(e -> {
            UserQuestionRecordsVo recordsVo = new UserQuestionRecordsVo();
            Long count = e.getValue().getLong("count");
            Long eCount = e.getValue().getLong("eCount");
            Long correct = count - eCount;
            recordsVo.setQCount(e.getValue().getLong("count"));
            recordsVo.setQuestionType(e.getKey());
            recordsVo.setComplete(correct);
            if (correct != 0) {
                recordsVo.setCorrectRate(BigDecimal.valueOf(correct).divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).intValue());
            } else {
                recordsVo.setCorrectRate(0);
            }
            vos.add(recordsVo);
        });

        return vos;

    }

    @Override
    @Transactional
    public void logoff(String userId) throws InterruptedException, TimeoutException {
        UserModel userModel = userService.getById(userId);
        userModel.setHeader("");
        userModel.setUserStatus(UserStatus.off);
        userModel.setName("已注销");
        userModel.setMobile("");
        userModel.setSex(Sex.unknown);
        userService.update(userModel);
        //脱离机构 组织 禁止登录
        memberMapper.physicsDelete(Wrappers.<MemberModel>lambdaUpdate().eq(MemberModel::getId, userId));
        //userGroupReService.unBind(userId);
        loginInfoMapper.physicsDelete(Wrappers.<LoginInfoModel>lambdaUpdate().eq(LoginInfoModel::getUserId, userId));
    }

    @Override
    @Transactional
    public void updateMember(RegisterMemberDto registerMemberDto) {
        registerMemberDto.setExpireTime(registerMemberDto.getExpireTime());
        UserModel userModel = new UserModel();
        BeanUtils.copyProperties(registerMemberDto, userModel);
        MemberModel memberModel = new MemberModel();
        BeanUtils.copyProperties(registerMemberDto, memberModel);
        boolean vers7 = userContext.currentPlatform().equals(PlatformType.VERS7);
        if (!vers7) {
            memberModel.setKaoyanExpireTime(memberModel.getExpireTime());
            memberModel.setExpireTime(null);
        }
        this.update(memberModel);
        userService.update(userModel);
        Long aLong = loginInfoService.selectCount(Wrappers.<LoginInfoModel>lambdaQuery().eq(LoginInfoModel::getLoginType, UserAccountType.username).ne(LoginInfoModel::getUserId, registerMemberDto.getId()).eq(LoginInfoModel::getAccount, registerMemberDto.getUserName()));
        if (aLong != null && aLong != 0) {
            throw new BizRuntimeException(UserResultCode.loginInfoExits);
        }
        LoginInfoModel loginInfoModel = new LoginInfoModel();
        loginInfoModel.setEnable(registerMemberDto.getUserStatus());
        loginInfoModel.setUserId(registerMemberDto.getId());
        loginInfoModel.setAccount(registerMemberDto.getUserName());
        if (StrUtil.isNotBlank(registerMemberDto.getPassword())) {
            loginInfoModel.setCertificate(LoginService.passwordEncryption(registerMemberDto.getPassword()));
        }
        loginInfoService.update(loginInfoModel, Wrappers.<LoginInfoModel>lambdaUpdate().eq(LoginInfoModel::getLoginType, UserAccountType.username).eq(LoginInfoModel::getUserId, registerMemberDto.getId()));
        //this.sendVers6(registerMemberDto.getId(), null, true);
    }

    @Override
    public Boolean sendVers6(String userId, String header, Boolean isSync) {
        try {
            if (null != isSync && !isSync) {
                return true;
            }
            log.info("发送给vers6开始");
            MemberModel model = this.getUserDetail(userId);
            OssUserModel userModel = new OssUserModel();
            userModel.setCreateTime(model.getCreatedTime());
            userModel.setSex(model.getSex());
            userModel.setDescription(model.getDescription());
            userModel.setStuNo(model.getStuNo());
            userModel.setName(model.getName());
            userModel.setEmail(model.getEmail());
            userModel.setMobile(model.getMobile());
            userModel.setCard(model.getCard());
            if (null != model.getBirthday()) {

                userModel.setBirthday(model.getBirthday());

            }
            if (StrUtil.isNotBlank(header)) {
                userModel.setHeader(headerUrl(header));

            }
            userModel.setUserStatus(model.getUserStatus().equals(UserStatus.normal));
            LoginInfoModel loginInfoModel = loginInfoService.selectUserLoginInfo(userId, UserAccountType.username);
            if (null != loginInfoModel) {
                userModel.setPassword(loginInfoModel.getCertificate());
                userModel.setUserName(loginInfoModel.getAccount());
            }
            GroupModel groupModel = groupService.getById(model.getGroupId());
//            if (null != groupModel) {
//                userModel.setGroupUserId(groupModel.getGroupCode());
//                userModel.setKaoyanExpireTime(groupModel.getKaoyanExpireTime());
//                userModel.setExpireTime(groupModel.getExpireTime());
//            }
            String s = this.encryptionVers(userModel);
            log.info("发送给vers6参数: {}", JSON.toJSONString(userModel));
            HttpRequest request = HttpUtil.createPost("https://langrunkaoyan.cqvip.com/Api/User/SynchronizeUserInfo");
            JSONObject object = new JSONObject();
            object.put("ossUserInfo", s);
            request.body(object.toString());
            request.timeout(500);
            HttpResponse response = request.execute();
            log.info("发送给vers6状态: {}", response.getStatus());
            response.close();

        } catch (Exception e) {
            log.error("同步到vers6: {}", e);
        }
        return true;
    }

    private String headerUrl(String header) {
        if (header.startsWith("cdn/vers7")) {
            return "https://vipcdn.caishi.cn/" + header;
        } else if (header.startsWith("cdn/langrun")) {
            return "https://vip-langrun-middle-cdn.oss-accelerate.aliyuncs.com/" + header;
        } else if (header.startsWith("static-cdn/")) {
            return "https://vipcdn.caishi.cn/" + header;
        } else {
            return "https://vip-langrun-middle-cdn.oss-accelerate.aliyuncs.com/" + header;
        }

    }

//    @Override
//    public Boolean rpcHaveType(String id) {
//        try {
//            UserModel byId = userService.getById(id);
//            byId.addHaveType(HaveType.ykt);
//            userService.update(byId);
//            return true;
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
//    }

//    @Override
//    public RpcMemberModel rpcSimpleUpdate(RpcMemberModel model) {
//        var member = BeanUtil.copyProperties(model, MemberModel.class);
//        return RpcMemberModel.model(this.simpleUpdate(member));
//    }

//    @Override
//    public RpcMemberModel rpcInsert(RpcMemberModel model) {
//        var member = BeanUtil.copyProperties(model, MemberModel.class);
//        return RpcMemberModel.model(insert(member));
//    }

//    @Override
//    public RpcGroupMemberUser rpcAdd(RpcGroupMemberUser model) throws Exception {
//        MemberModel memberModel = new MemberModel();
//        memberModel.addUserType(UserType.member);
//        memberModel.setSex(null != model.getSex() ? model.getSex() : Sex.unknown);
//        memberModel.setHeader(model.getHeader());
//        memberModel.setName(model.getName());
//        memberModel.setStuNo(model.getStuNo());
//        memberModel.setMobile(model.getMobile());
//        memberModel.setChannelType(UserChannelType.valueOf(model.getChannelType().name()));
//        memberModel.setHaveType(model.getHaveType());
//        LoginInfoModel loginInfoModel = new LoginInfoModel();
//        loginInfoModel.setCertificate(StrUtil.isBlank(model.getCertificate()) ? diamondConfig.getUserDefaultPassword() : model.getCertificate());
//        loginInfoModel.setLoginType(UserAccountType.username);
//        loginInfoModel.setAccount(StrUtil.isBlank(model.getAccount()) ? memberModel.getMobile() : model.getAccount());
//        IUserModel<?> userModel = loginServiceComposite.register(loginInfoModel, memberModel);
//        if (null != model) {
//            model.setId(userModel.getId());
//            return model;
//        } else {
//            return null;
//
//        }
//    }

//    @Override
//    public RpcMemberModel rpcGetUserDetail(String id) {
//        MemberModel userDetail = this.getUserDetail(id);
//        if (userDetail != null) {
//            return RpcMemberModel.model(userDetail);
//        } else {
//            return null;
//        }
//    }

//    @Override
//    public RpcMemberModel rpcSelectOne(String memberId) {
//        var model = this.selectOne(Wrappers.<MemberModel>lambdaQuery().eq(MemberModel::getId, memberId));
//        return RpcMemberModel.model(model);
//    }

    public Integer questionCount(String userId) {
        return userCountRecordsService.userTodayData(userId).getDc();
    }

//    @Override
//    public Boolean rpcLogoff(String userId) {
//        try {
//
//            this.logoff(userId);
//
//        } catch (Exception e) {
//            log.error("注销账号失败", e);
//            return false;
//        }
//        return true;
//    }

//    @Override
//    public RpcMemberModel rpcUpdate(RpcMemberModel rpcMemberModel) {
//        var model = BeanUtil.copyProperties(rpcMemberModel, MemberModel.class);
//        return BeanUtil.copyProperties(update(model), RpcMemberModel.class);
//    }

//    @Override
//    public Map<String, Object> rpcSearch(Map<String, Object> param, long total, long current, long size, boolean btotal) {
//        Page<MemberModel> p = new Page<>();
//        p.setTotal(total);
//        p.setSize(size);
//        p.setCurrent(current);
//        var list = search(param, p, btotal);
//        List<MemberModel> records = list.getRecords();
//        var rpcList = records.stream().map(v -> BeanUtil.copyProperties(v, RpcMemberModel.class)).collect(Collectors.toList());
//        Map<String, Object> map = new HashMap<>();
//        map.put("current", list.getCurrent());
//        map.put("total", list.getTotal());
//        map.put("size", list.getSize());
//        map.put("list", rpcList);
//        return map;
//    }

//    @Override
//    public List<RpcMemberModel> rpcSelectBatchIn(List<String> ids) {
//        var list = memberMapper.selectBatchIds(ids);
//        var models = list.stream().map(v -> {
//            UserModel base = userService.getById(v.getId());
//            return v.userInfoCopy(base).clearEmptyStringField("expireTime");
//        }).toList();
//        return models.stream().map(v -> BeanUtil.copyProperties(v, RpcMemberModel.class)).collect(Collectors.toList());
//    }

    @Override
    public MemberModel fetchVersRegister(String userAccount) throws BizException {
        String post = HttpUtil.post(VERS_USERINFO_URL + userAccount, new HashMap<String, Object>());
        JSONObject body = JSON.parseObject(post);
        log.info("拉起vers数据:{}", post);
        OssUserModel ossUserModel = this.decryptionVers(body.getString("data"));
        String userId = this.getUserIdByssoUserModel(ossUserModel);
        return this.saveVers6(ossUserModel, userId);
    }

    @Override
    public List<Map> countLine(Map<String, Object> param) {
        return memberMapper.countLine(param);
    }

    @Override
    public OssUserModel decryptionVers(String code) {
        String md5 = MD5Util.MD5Encode(OSS_USER_INFO + OSS_DOMAIN);
        var aes = new AES(Mode.ECB, Padding.ZeroPadding, md5.getBytes(StandardCharsets.UTF_8));
        JSONObject jsonObject = JSON.parseObject(aes.decryptStr(code));
        jsonObject.put("birthday", this.processDate(jsonObject.getString("birthday")));
        jsonObject.put("kaoYanExpireTime", this.processDate(jsonObject.getString("kaoYanExpireTime")));
        jsonObject.put("expireTime", this.processDate(jsonObject.getString("expireTime")));
        return jsonObject.toJavaObject(OssUserModel.class);
    }
    @Override
    public OssUserModel decryptionCqrspx(String code) {
        String md5 = MD5Util.MD5Encode(OSS_USER_INFO + OSS_DOMAIN);
        var aes = new AES(Mode.ECB, Padding.ZeroPadding, md5.getBytes(StandardCharsets.UTF_8));
        JSONObject jsonObject = JSON.parseObject(aes.decryptStr(code));
        return jsonObject.toJavaObject(OssUserModel.class);
    }



    private LocalDate processDate(String timestampStr) {
        if (StrUtil.isBlank(timestampStr)) return null;
        LocalDate localDate = null;
        try {
            long timestamp = Long.parseLong(timestampStr);
            Instant instant;
            if (timestampStr.length() > 10) {
                instant = Instant.ofEpochMilli(timestamp);
            } else {
                instant = Instant.ofEpochSecond(timestamp);
            }
            localDate = instant.atZone(ZoneOffset.ofHours(8)).toLocalDate();
        } catch (Exception e) {
            log.error("时间戳转换失败:{}", timestampStr, e);
        }
        if (null == localDate) {
            localDate = LocalDate.parse(timestampStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }
        return localDate;
    }

    //{"createTime":1690435428000,"expireTime":1767110400000,"groupUserId":"62a2dbcb-0509-4762-a430-6a9b8281ca7f","kaoyanExpireTime":1767110400000,"mobile":"18716597862","name":"维普学员22123","password":"Vip123456","sex":"unknown","stuNo":"VIP0543737","type":"pcWeb6","userName":"18716597862","userStatus":true}
    public static void main(String[] args) {
      MemberServiceImpl memberService = new MemberServiceImpl();
        OssUserModel model = new OssUserModel();

        model.setUserName("rspxidtest1112");
        model.setName("王五");
        System.out.println(memberService.encryptionVers(model));
        System.out.println(MD5Util.MD5Encode(OSS_USER_INFO + OSS_DOMAIN));
        //System.out.println( DataEncryptionHandler.decrypt("04bba87803d5e1271e28621d78998fe32b831d8f7af41006d2551074ed09597a5d2d5f1df2b547e22a99f1e63daa6145ec307d5d0fd6c7"));
    }

    @Override
    public String encryptionVers(OssUserModel ossUserModel) {
        String md5 = MD5Util.MD5Encode(OSS_USER_INFO + OSS_DOMAIN);
        var aes = new AES(Mode.ECB, Padding.ZeroPadding, md5.getBytes(StandardCharsets.UTF_8));
        return aes.encryptBase64(JSON.toJSONString(ossUserModel));
    }


    @Override
    public String getUserIdByssoUserModel(OssUserModel userModel) {
        LoginInfoModel loginInfoModel = loginInfoService.selectLoginInfoUserAccount(userModel.getUserName(), UserAccountType.username);
        String userId = null;
        if (null == loginInfoModel) {
            UserModel userByMobile = this.getUserByMobile(userModel.getMobile());
            if (null != userByMobile) {
                userId = userByMobile.getId();
            }
        } else {
            userId = loginInfoModel.getUserId();
        }
        return userId;
    }

    @Override
    @Transactional
    public MemberModel saveVers6(OssUserModel userModel, String userId) throws BizException {
        log.info("保存vers6开始 ossUserModel:{}---{}", userId, JSON.toJSONString(userModel));
        MemberModel memberModel = new MemberModel();
        memberModel.setEmail(userModel.getEmail());
        memberModel.setStuNo(userModel.getStuNo());
        memberModel.setDepartmentId(userModel.getDepartmentId());
        memberModel.setSex(userModel.getSex());
        memberModel.setMobile(userModel.getMobile());
        UserChannelType type = userModel.getType();

        if (userModel.getUserName().length() > 11) {
            memberModel.setName(userModel.getUserName().substring(0, 11));
        } else {
            memberModel.setName(userModel.getName());
        }
        memberModel.setCard(userModel.getCard());
        memberModel.setDescription(userModel.getDescription());
        memberModel.setBirthday(userModel.getBirthday());
        String groupUserId = userModel.getGroupUserId();
        if (StrUtil.isNotBlank(userModel.getHeader())) {
            try {
                String fileName = userModel.getHeader().substring(userModel.getHeader().lastIndexOf("/") + 1);
                String reg = "^.*\\.*(?=\\?)";
                Pattern pat = Pattern.compile(reg);
                Matcher mat = pat.matcher(fileName);
                if (mat.find()) {
                    fileName = mat.group();
                }
                memberModel.setHeader(cdnOssPlugins.webFileUploadSync(userModel.getHeader(), fileName));
            } catch (Exception e) {
                log.error("头像上传失败:{}", userModel.getHeader());

            }
        }
        LoginInfoModel loginInfo = new LoginInfoModel();
        loginInfo.setCertificate(userModel.getPassword().trim());
        loginInfo.setLoginType(UserAccountType.username);
        loginInfo.setAccount(userModel.getUserName());
        loginInfo.setUserId(userId);
        if (StrUtil.isNotBlank(userId)) {
            memberModel.setId(userId);
            log.info("保存vers6 model: {}", memberModel);
            this.update(memberModel);
            if (StrUtil.isBlank(groupUserId) || groupUserId.trim().equals("0")) {
                log.info("解绑groupUserId: {}", groupUserId);
                UserModel user = userService.getById(userId);
                Optional<UserType> first = Arrays.stream(user.getUserTypes()).filter(f -> f.equals(UserType.member)).findFirst();
                if (first.isEmpty()) {
                    user.addUserType(UserType.member);
                }
                userService.update(user);
                this.update(null, Wrappers.<MemberModel>lambdaUpdate().eq(MemberModel::getId, userId).setSql(" group_id = null , expire_time = null ,kaoyan_expire_time = null"));
            } else {
                groupUserId = groupUserId.trim();
                if (null != userModel.getKaoyanExpireTime() && userModel.getKaoyanExpireTime().getYear() != 1900) {
                    memberModel.setKaoyanExpireTime(userModel.getKaoyanExpireTime().atTime(23, 59, 59));
                }
                if (null != userModel.getExpireTime() && userModel.getExpireTime().getYear() != 1900) {
                    memberModel.setExpireTime(userModel.getExpireTime().atTime(23, 59, 59));
                }
                this.bindGroupCode(memberModel, groupUserId);
            }
            LoginInfoModel infoModel = loginInfoService.selectUserLoginInfo(userId, UserAccountType.username);
            loginInfoService.updateAccountByUserId(userId, memberModel.getMobile(), UserAccountType.phone);
            if (null == infoModel) {
                loginInfoService.insert(loginInfo);
            } else {
                loginInfo.setId(infoModel.getId());
                loginInfoService.update(loginInfo);
            }

        } else {
            UserModel userByMobile = this.getUserByMobile(userModel.getMobile());
            if (null != userByMobile) {
                this.saveVers6(userModel, userByMobile.getId());
            }
            memberModel.setChannelType(null != type ? type : UserChannelType.pcWeb6);
            memberModel.setIsSyncVers(false);
            memberModel.addUserType(UserType.member);
            log.info("注册vers6来源数据: {}", memberModel);
            loginServiceComposite.register(loginInfo, memberModel);

            if (StrUtil.isNotBlank(groupUserId) && !groupUserId.trim().equals("0")) {
                groupUserId = groupUserId.trim();
                log.info("注册vers6绑定机构: {}", groupUserId);
                if (null != userModel.getKaoyanExpireTime() && userModel.getKaoyanExpireTime().getYear() != 1900) {
                    memberModel.setKaoyanExpireTime(userModel.getKaoyanExpireTime().atTime(23, 59, 59));
                }
                if (null != userModel.getExpireTime() && userModel.getExpireTime().getYear() != 1900) {
                    memberModel.setExpireTime(userModel.getExpireTime().atTime(23, 59, 59));
                }
                this.bindGroupCode(memberModel, groupUserId);

            }
        }

        return memberModel;
    }

    @Override
    @Transactional
    public MemberModel saveCqrspx(OssUserModel userModel, String userId) throws BizException {
        log.info("保存重庆人社培训网用户开始 ossUserModel:{}",  JSON.toJSONString(userModel));
        MemberModel memberModel = new MemberModel();
        memberModel.setName(userModel.getName());
        memberModel.setGroupId(userModel.getGroupId());

        LoginInfoModel loginInfo = new LoginInfoModel();
        loginInfo.setLoginType(UserAccountType.orgLogin);
        loginInfo.setAccount(userModel.getUserName());

        memberModel.setChannelType(UserChannelType.cqrspxzx);
        memberModel.setIsSyncVers(false);
        memberModel.addUserType(UserType.member);
        log.info("注册重庆人社培训网来源数据: {}", memberModel);
        loginServiceComposite.register(loginInfo, memberModel);

        return memberModel;
    }

}
